<template>
  <div class="box unselectable undraggable" ref="box">
    <div class="left">
      <el-card shadow="never">
        <template #header>
          <div class="card-header">
            <span>场景管理</span>
          </div>
        </template>
        <!-- 左侧树 -->
        <treeCom ref="treeRef" @add-directory="handleAddDirectory" @edit-directory="handleEditDirectory"
          @create-scene="handleCreateSence" @delete-node="handelDeleteNode" @edit-scene="handleEditSence"
          @node-click="handleNodeClick" :currentTabId="editableTabsId" />
      </el-card>
    </div>
    <div class="resize" title="左右侧边栏">⋮</div>
    <div class="mid" v-loading="loading">
      <el-tabs v-model="editableTabsValue" type="card" class="demo-tabs" :addable="isShowAdd" ref="tabsRef"
        @tab-change="handleTabChange" @tab-remove="removeTab" :before-leave="beforeLeave" @tab-add="addTab">
        <el-tab-pane :closable="false" v-if="!editableTabs.length" name="new">
          <template #label>
            <span class="custom-tabs-label">
              <span>新建...</span>
            </span>
          </template>
          <div class="center">
            <p>从左侧选择一个资源</p>
            <p>或</p>
            <p><el-button type="primary" plain @click="handleCreateSence">新建测试场景</el-button></p>
          </div>
        </el-tab-pane>
        <el-tab-pane v-for="item in editableTabs" :key="item.name" :name="item.name"
          :closable="editableTabs.length > 1">
          <template #label>
            <span class="custom-tabs-label">
              <img src="@/assets/svgs/scene.svg" class="case-icon" />
              <span>{{ item.title }}</span>
              <span class="dot" v-if="item.save === false"></span>
            </span>
          </template>
          <sceneContent :data="item.data" @steps="handleSteps" @base-info="handleBaseInfo" @open-env="openDialog" />
        </el-tab-pane>
        <!-- 尾随在页签后面的添加按钮 -->
        <el-tab-pane :closable="false" name="add" class="add-pane" v-if="!isShowAdd">
          <template #label>
            <el-icon @click.stop="handleCreateSence">
              <Plus />
            </el-icon>
          </template>
        </el-tab-pane>
        <!-- 固定在右侧的添加按钮 -->
        <template #add-icon v-if="isShowAdd">
          <el-icon @click.stop="handleCreateSence">
            <Plus />
          </el-icon>
        </template>
      </el-tabs>
      <div class="environment-select">
        <el-select style="width: 120px;margin-right: 10px;" v-model="environment" placeholder="请选择环境配置"
          @change="changeEnv">
          <el-option v-for="item in environments" :key="item.id" :label="item.name" :value="item.id" />
          <template #footer>
            <el-button type="primary" plain :icon="Setting" @click="openDialog()">管理环境</el-button>
          </template>
        </el-select>
        <el-button type="primary" @click="execute"
          :disabled="!saveStepFlag || !environmentId || !currentTabData?.steps?.length"
          :loading="btnLoading">执行</el-button>
        <el-button type="primary" plain @click="save">保存</el-button>
      </div>
    </div>
    <!-- 表单弹窗：添加目录 -->
    <AddDicrectory ref="addDicrectoryRef" @success="handleUpdateDicSuccess" />
    <!-- 新增、重命名场景 -->
    <AddScene ref="addSceneRef" @success="handleUpdateSceneSuccess" />
    <!-- 环境弹窗 -->
    <environmentDialog ref="environmentRef" @success="getEnvironments" />
  </div>
</template>

<script setup lang="ts">
import * as environmentApi from '@/api/environment'
import * as sceneApi from '@/api/scene'
import { Plus, Link, Setting } from '@element-plus/icons-vue'
import { useAppStore } from '@/store/modules/app'
import treeCom from './components/treeCom.vue';
import sceneContent from './module/sceneContent.vue';
import AddDicrectory from './dialog/AddDicrectory.vue';
import AddScene from './dialog/AddScene.vue';
import environmentDialog from '../debug/environment/environmentDialog.vue';
import { useDebugStore } from '@/store/modules/debug'
import { ElMessage } from 'element-plus'
import { isValidDateTime } from '@/utils/dateUtil'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const appStore = useAppStore()
const debugStore = useDebugStore()
let projectId = computed(() => appStore.getSelectProject)
const environmentId = computed(() => debugStore.getEnvironmentId)
let tabIndex = 0
const tabsRef = ref(null);
const addDicrectoryRef = ref()
const treeRef = ref()
const addSceneRef = ref()
const isShowAdd = ref(false)
const editableTabsValue = ref('new')
const editableTabs = ref([] as any)
const loading = ref(false)
/** 当前tab */
const currentTab = computed(() => {
  return editableTabs.value.find(tab => tab.name === editableTabsValue.value);
});
const currentTabData = computed(() => currentTab.value?.data)
const saveStepFlag = computed(() => currentTab.value?.saveStep)
const editableTabsId = computed(() => Number(currentTab.value?.id))
const btnLoading = ref(false)
const stepStyle = {
  '接口': 'API',
  '用例': 'API_CASE',
  'time': 'CONSTANT_TIMER',
  '自定义请求': 'CUSTOM_REQUEST'
}
watch(() => projectId.value, async () => {
  environment.value = ''
  debugStore.setEnvironmentId('')
  debugStore.setEnvironmentVars([])
  debugStore.setEnvironments([])
  getEnvironments()
  editableTabs.value = []
  editableTabsValue.value = 'new'
})

/**
 * 环境变量操作
 * */
const environment = ref('')
const environments = ref([])
const environmentRef = ref()
const getEnvironments = async () => {
  let res = await environmentApi.getEnvironmentList()
  environments.value = res
  debugStore.setEnvironments(res)
}
const openDialog = () => {
  environmentRef.value.open()
}
const changeEnv = (val) => {
  debugStore.setEnvironmentId(val)
}
const handlePostData = (list) => {
  if (!list || !list.length) return []
  return list.map(item => {
    return {
      stepId: item.stepId,
      type: item.type,
      status: item.status ? 0 : 1
    }
  })
}
/** 基本信息内容变化同步到tab页签 */
const handleBaseInfo = (data) => {
  editableTabs.value.forEach(item => {
    if (item.name == editableTabsValue.value) {
      item.title = data.baseInfo.name
      item.save = false
      item.saveInfo = false
      item.data = {
        ...item.data,
        ...data.baseInfo
      }
    }
  })
}
/** 步骤内容变化同步到tab页签 */
const handleSteps = (data) => {
  editableTabs.value.forEach(item => {
    if (item.name == editableTabsValue.value) {
      item.save = false
      item.saveStep = false
      item.data = {
        ...item.data,
        steps: data.steps
      }
    }
  })
}

/** 执行 */
const execute = async () => {
  message.info('执行中，请稍等！')
  btnLoading.value = true
  try {
    const params = {
      projectId: projectId.value,
      environmentId: environmentId.value,
      sceneId: currentTabData.value.id
    }
    await sceneApi.runTestScene(params)
    ElMessage.closeAll()
    setTimeout(() => {
      message.success('执行成功！')
    })
    btnLoading.value = false
  } catch (error) {
    ElMessage.closeAll()
    setTimeout(() => {
      message.error('执行失败！')
    })
  } finally {
    btnLoading.value = false
  }
}
/** 保存 */
const save = async () => {
  loading.value = true
  try {
    await saveBaseInfo()
    await saveStep()
    message.success(t('common.updateSuccess'))
    // 是否保存标签重置
    editableTabs.value.forEach(item => {
      if (item.name == editableTabsValue.value) {
        item.save = true
        item.saveStep = true
        item.saveInfo = true
      }
    })
  } catch (error) {

  } finally {
    loading.value = false
  }
}
/** 保存步骤 */
const saveStep = async () => {
  if (currentTab.value.saveStep) return
  const steps = currentTabData.value.steps.map(item => {
    // 已经保存的步骤存在config,单独处理
    if (item.config) {
      return {
        sceneId: item.sceneId,
        projectId: projectId.value,
        apiId: item.apiId,
        apiCaseId: item.apiCaseId,
        name: item.name,
        status: item.status ? 0 : 1,
        stepType: item.stepType,
        config: JSON.parse(item.config)
      }
    } else {
      let config = {}
      if (['接口', '用例'].includes(item.tag)) {
        config = { source: item }
      } else if (item.tag == 'time') {
        config = {
          delay: item.delay
        }
      } else if (item.tag == '自定义请求') {
        let paramsValue = []
        if (['form-data', 'x-www-form-urlencoded'].includes(item.queryInfo.requestContentType) && item.queryInfo.bodyForm.length) {
          paramsValue = item.queryInfo.bodyForm || []
          paramsValue.forEach(item => {
            if (isValidDateTime(item.keyValue)) {
              item.keyValue = encodeURI(item.keyValue)
            }
          })
        } else {
          paramsValue = item.queryInfo.params || []
          paramsValue.forEach(item => {
            if (isValidDateTime(item.keyValue)) {
              item.keyValue = encodeURI(item.keyValue)
            }
          })
        }
        // headers处理 1.存在auth,Headers中有Authorization时进行替换，没有时进行添加2.不存在auth时，直接使用Headers
        let headers = [] as any
        if (item.queryInfo.auth) {
          if (!item.queryInfo.headers?.length) {
            headers = [{
              keyName: 'Authorization',
              keyValue: item.queryInfo.auth,
              keyType: 'string'
            }]
          } else {
            const isHasAuthorization = item.queryInfo.headers.some(q => q.keyName == 'Authorization')
            if (isHasAuthorization) {
              headers = item.queryInfo.headers.map(i => {
                if (i.keyName == 'Authorization') {
                  return {
                    ...i,
                    keyValue: item.queryInfo.auth,
                    keyType: 'string'
                  }
                }
                return i
              })
            } else {
              headers = [...(item.queryInfo.headers || []), {
                keyName: 'Authorization',
                keyValue: item.queryInfo.auth,
                keyType: 'string'
              }]
            }
          }
        } else {
          headers = [...(item.queryInfo.headers || [])]
        }
        config = {
          url: item.queryInfo.query.url || "",
          requestContentType: item.queryInfo.requestContentType || "",
          requestType: item.queryInfo.query.requestType || "",
          requestHeaderList: headers || [],
          requestParamsList: paramsValue || [],
          requestBodyList: [],
          requestBody: item.queryInfo.body || "",
          auth: item.queryInfo.auth || "",
          post: item.queryInfo.post,
          postProcessors: handlePostData(item.queryInfo.post)
        }
      }
      return {
        sceneId: currentTabData.value.id,
        projectId: projectId.value,
        apiId: item.tag == '接口' ? item.id : '',
        apiCaseId: item.tag == '用例' ? item.id : '',
        name: item.name,
        status: item.status ? 0 : 1,
        stepType: stepStyle[item.tag],
        config
      }
    }
  })
  await sceneApi.createSceneStep({ sceneId: currentTabData.value.id, steps })
}
/** 保存基本信息 */
const saveBaseInfo = async () => {
  if (currentTab.value.saveInfo) return
  const params = {
    id: currentTabData.value.id,
    projectId: currentTabData.value.projectId,
    name: currentTabData.value.name,
    priority: currentTabData.value.priority,
    description: currentTabData.value.description,
    directoryId: currentTabData.value.directoryId
  }
  await sceneApi.updateTestScene(params)
  // 更新树数据
  treeRef.value.getTreeData()
}
/**
 * 切换tab时触发，检查是否需要切换添加按钮
 * */
const handleTabChange = () => {
  checkScrollButtons();
}
/**
 * 新增tab页签
 * */
const addTab = (info?) => {
  const newTabName = `${++tabIndex}`
  editableTabs.value.push({
    id: info.id,
    title: info.name,
    name: newTabName,
    save: true,
    saveStep: true,
    saveInfo: true,
    data: info.data  // 场景信息
  })
  editableTabsValue.value = newTabName
  checkScrollButtons()
}
/**
 * 删除tab页签并检查是否更新新增按钮位置
 * */
const removeTab = async (targetName: string) => {
  try {
    const tabs = editableTabs.value
    // 判断删除页签是否未保存
    const delTab = tabs.find(t => t.name == targetName)
    if (!delTab.save) {
      // 删除的二次确认
      await message.delConfirm('当前标签页未保存，确定关闭吗？')
    }
    let activeName = editableTabsValue.value
    if (activeName === targetName) {
      tabs.forEach((tab, index) => {
        if (tab.name === targetName) {
          const nextTab = tabs[index + 1] || tabs[index - 1]
          if (nextTab) {
            activeName = nextTab.name
          } else {
            activeName = 'new'
          }
        }
      })
    }

    editableTabsValue.value = activeName
    editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
    checkScrollButtons()
  } catch (error) { }

}

/* 活动标签切换时触发，用于点击尾随添加按钮时，添加页签而非选中 */
const beforeLeave = (currentName) => {
  if (currentName == "add") {
    addTab()
    return false
  } else {
    editableTabsValue.value = currentName
  }
}
/** 目录操作成功后更新树结构 */
const handleUpdateDicSuccess = () => {
  treeRef.value.getTreeData()
}
/** 场景操作成功后 */
const handleUpdateSceneSuccess = (data) => {
  treeRef.value.handleFocusNode(data)
}
/** 新增目录 */
const handleAddDirectory = (data) => {
  addDicrectoryRef.value.open('create', data, '新建目录')
}
/** 目录重命名 */
const handleEditDirectory = (data) => {
  addDicrectoryRef.value.open('update', data, '重命名')
}
/** 新增测试场景 */
const handleCreateSence = (data) => {
  addSceneRef.value.open('create', data, '新建测试场景')
}
/** 编辑测试场景 */
const handleEditSence = (data) => {
  addSceneRef.value.open('update', data, '重命名')
}
/** 点击场景节点 */
const handleNodeClick = async (treeData, node) => {
  // 判断页签是否存在，存在则定位，否则新增
  const isHas = editableTabs.value.find(e => e.id == node.id)
  if (isHas) {
    editableTabsValue.value = isHas.name
  } else {
    let res = await sceneApi.getTestScene({ id: node.id })
    addTab({
      ...node,
      data: res
    })
  }
}
/** 删除节点对应tab */
const handelDeleteNode = (info) => {
  const tabs = editableTabs.value
  let activeId = info.id
  let activeName = editableTabsValue.value
  tabs.forEach((tab, index) => {
    if (tab.id === activeId) {
      const nextTab = tabs[index + 1] || tabs[index - 1]
      if (nextTab) {
        activeName = nextTab.name
      } else {
        activeName = 'new'
      }
    }
  })
  editableTabsValue.value = activeName
  editableTabs.value = tabs.filter((tab) => tab.id !== activeId)
  checkScrollButtons()
}
/**
 * 检查新增按钮是否需要换到最右侧
 * */
const checkScrollButtons = async () => {
  await nextTick()
  const tabsEl = tabsRef.value.$el;
  const tabsHeader = tabsEl.querySelector('.el-tabs__header')
  const tabsNav = tabsEl.querySelector('.el-tabs__nav')
  const tabsHeaderWidth = tabsHeader.clientWidth
  const tabsNavWidth = tabsNav.clientWidth;
  isShowAdd.value = tabsNavWidth + 500 > tabsHeaderWidth
}
// 左右拖动事件
function dragControllerLR() {
  const resize = document.getElementsByClassName("resize");
  const left = document.getElementsByClassName("left");
  const mid = document.getElementsByClassName("mid");
  const box = document.getElementsByClassName("box");
  for (let i = 0; i < resize.length; i++) {
    // 鼠标按下事件
    resize[i].onmousedown = function (e) {
      //颜色改变提醒
      resize[i].style.background = "#818181";
      const startX = e.clientX;
      resize[i].left = resize[i].offsetLeft;
      // 鼠标拖动事件
      document.onmousemove = function (e) {
        const endX = e.clientX;
        let moveLen = resize[i].left + (endX - startX); // （endx-startx）=移动的距离。resize[i].left+移动的距离=左边区域最后的宽度
        const maxT = box[i].clientWidth - resize[i].offsetWidth; // 容器宽度 - 左边区域的宽度 = 右边区域的宽度

        if (moveLen < 50) moveLen = 50; // 左边区域的最小宽度为50px
        if (moveLen > maxT - 150) moveLen = maxT - 150; //右边区域最小宽度为150px

        resize[i].style.left = moveLen; // 设置左侧区域的宽度

        for (let j = 0; j < left.length; j++) {
          left[j].style.width = moveLen + "px";
          mid[j].style.width = box[i].clientWidth - moveLen - 10 + "px";
        }
      };
      // 鼠标松开事件
      // eslint-disable-next-line no-unused-vars
      document.onmouseup = function (evt) {
        //颜色恢复
        resize[i].style.background = "#d6d6d6";
        document.onmousemove = null;
        document.onmouseup = null;
        resize[i].releaseCapture && resize[i].releaseCapture(); //当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉
      };
      resize[i].setCapture && resize[i].setCapture(); //该函数在属于当前线程的指定窗口里设置鼠标捕获
      return false;
    };
  }
}
onMounted(() => {
  dragControllerLR();
  environment.value = debugStore.getEnvironmentId
  if (projectId.value) {
  }
});
</script>

<style scoped lang="scss">
.center {
  text-align: center;
  margin-top: 100px;
}

:deep(.memorize_new_word) {
  text-decoration-color: transparent;
}

.dot {
  width: 10px;
  height: 10px;
  display: inline-block;
  border-radius: 100%;
  background: #409eff;
  margin-left: 5px;
}

:deep(#tab-add) {
  width: 40px;
  height: 40px;
  background: var(--el-bg-color);
  position: relative;
  top: -5px;
  right: -1px;
  padding: 0;
}

.environment-select {
  display: flex;
  position: absolute;
  right: 5px;
  top: 3px;

  .el-select {
    .el-select__wrapper {
      border-radius: 4px 0 0 4px !important;
    }
  }

}

:deep(.demo-tabs) {
  height: 100%;

  >.el-tabs__header {
    .el-tabs__nav {
      border: none;

      .el-tabs__item {
        background: var(--el-bg-color-page);
        height: 30px;
        margin-top: 5px;
        border-radius: 3px;
        border: none;
        margin-right: 5px;
      }

      .el-tabs__item.is-active {
        background: #409eff22;
      }
    }
  }

  .custom-tabs-label {
    display: flex;
    align-items: center;
  }

  .el-tabs__new-tab {
    margin-right: 277px;
  }

  .el-tabs__content {
    height: calc(100% - 54px);

    .el-tab-pane {
      height: 100%;

      .query-com {
        height: calc(100% - 32px);

        .el-tabs {
          height: 100%;
        }
      }

      .response {
        height: 100%;
        width: 100%;
        position: relative;

        .result {
          position: absolute;
          right: 10px;
          top: 0px;
          display: flex;
          width: 200px;
          justify-content: space-between;
          font-size: 12px;
          align-items: center;
          height: 40px;

          &-text {
            display: inline-block;
            padding: 2px 5px;
            border-radius: 4px;
          }
        }

        .el-tabs {
          height: 100%;
        }
      }
    }
  }
}

:deep(#tab-add:hover) {
  color: #333;
  cursor: pointer;
}

:deep(.el-card) {
  height: 100%;
}

:deep(.el-card__body) {
  padding: 10px !important;
  height: calc(100% - 78px);

  >div {
    height: 100%;
  }

  .custom-tree-node {
    display: flex;
    justify-content: space-between;
    width: calc(100% - 20px);
    height: 100%;
    line-height: 26px;

    >span:first-child {
      width: calc(100% - 25px);

      small {
        overflow: hidden;
        width: calc(100% - 25px);
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }

    .operation {
      display: none;
      margin-right: 15px;
    }

    &:hover {
      .operation {
        display: block;
      }
    }
  }
}

:deep(.el-tree.tree-com) {
  height: calc(100% - 45px);
  overflow: hidden;

  >.el-tree-node {
    height: 100%;
    overflow: auto;
    // position: relative;
    // right: -10px;
  }
}

.method {
  margin-right: 10px;
}

/*包围div样式*/
.box {
  width: 100%;
  overflow: hidden;
  display: flex;
  background: var(--el-bg-color);
  height: calc(100vh - 175px);
}

/*左侧div样式*/
.left {
  width: calc(25% - 10px);
  /*左侧初始化宽度*/
  height: 100%;
  float: left;
  position: relative;

  :deep(.el-card__header) {
    padding: 10px !important;
  }
}

/* 拖拽区div样式 */
.resize {
  cursor: w-resize;
  float: left;
  position: relative;
  top: 45%;
  background-color: #d6d6d6;
  border-radius: 5px;
  margin-top: -10px;
  width: 10px;
  height: 50px;
  background-size: cover;
  background-position: center;
  /*z-index: 99999;*/
  font-size: 32px;
  color: white;
}

/*拖拽区鼠标悬停样式*/
.move:hover {
  color: #444444;
}

/*右侧div'样式*/
.mid {
  float: left;
  width: 75%;
  /*右侧初始化宽度*/
  height: 100%;
  box-shadow: -1px 4px 5px 3px rgba(0, 0, 0, 0.11);
  background: var(--el-bg-color);
  padding-left: 10px;
  box-sizing: border-box;
  flex: 1;
  position: relative;
}
</style>
